import * as babel from '@babel/core';
import {CodePlatformHumpMethod, CodePlatformMethod} from "./types";
import {CodePlatformMethodMap} from "./constant";

/**
 * Babel插件所需类型
 */
interface BabelPath {
  node: any;
  parent: any;
  replaceWith: (node: any) => void;
  findParent: (callback: (path: any) => boolean) => any;
}

// 获取当前构建平台
const getPlatform = () => {
  // @ts-ignore
  const platform = process.env.UNI_PLATFORM || '';

  // 平台映射
  const platformMap = {
    'app': 'APP',
    'h5': 'WEB',
    'mp-alipay': 'MP_ALIPAY',
    'mp-baidu': 'MP_BAIDU',
    'mp-jd': 'MP_JD',
    'mp-kuaishou': 'MP_KUAISHOU',
    'mp-lark': 'MP_LARK',
    'mp-qq': 'MP_QQ',
    'mp-toutiao': 'MP_TOUTIAO',
    'mp-weixin': 'MP_WEIXIN',
    'mp-360': 'MP_360',
    'mp-xhs': 'MP_XHS',
    'quickapp-webview': 'QUICKAPP_WEBVIEW',
    'quickapp-webview-union': 'QUICKAPP_WEBVIEW_UNION',
    'quickapp-webview-huawei': 'QUICKAPP_WEBVIEW_HUAWEI',
  };

  // @ts-ignore
  return platformMap[platform] || 'WEB';
};

/**
 * 平台代码转换器 - 用于在编译时移除非当前平台的代码块
 *
 * 在uni-app构建时使用方法：
 *
 * 1. Vite项目 (vite.config.js):
 * ```js
 * import { PlatformTransformer } from 'uni-platform-chain'
 *
 * export default defineConfig({
 *   plugins: [
 *     PlatformTransformer.createVitePlugin(),
 *     uni(),
 *   ],
 * })
 * ```
 *
 * 2. Vue CLI项目 (vue.config.js):
 * ```js
 * const { PlatformTransformer } = require('uni-platform-chain');
 *
 * module.exports = {
 *   chainWebpack: config => {
 *
 *     // 添加loader
 *     config.module
 *       .rule('js')
 *       .use('babel-loader')
 *       .tap(options => {
 *         options.plugins = options.plugins || [];
 *         options.plugins.push(PlatformTransformer.createBabelPlugin());
 *         return options;
 *       });
 *   }
 * }
 * ```
 */
export class PlatformTransformer {
  /**
   * 创建Babel插件，用于移除非当前平台的代码块
   * @param platform 平台
   * @returns Babel插件对象
   */
  public static createBabelPlugin(platform: CodePlatformMethod) {
    const currentPlatform = platform || getPlatform();
    const isMp = platform ? platform === 'MP' : currentPlatform.startsWith('MP_');

    // 返回一个Babel插件配置
    return function () {
      return {
        visitor: {
          // 查找并处理exe()调用，这样能捕获整个链式调用
          CallExpression(path: BabelPath) {
            // 检查是否是exe()调用
            if (
              path.node.callee.type === 'MemberExpression' &&
              path.node.callee.property.type === 'Identifier' &&
              path.node.callee.property.name === 'exe'
            ) {
              // 找到链式调用的起点（PlatformChain.with）
              let chainStart = null;
              let currentNode = path.node.callee.object;
              const platformCallbacks = new Map<string, any>();
              let defaultCallback = null;

              // 获取exe调用的参数，用于传递给选中的回调函数
              const exeArguments = path.node.arguments || [];

              // 回溯查找链式调用的起点和所有平台方法
              while (currentNode && currentNode.type === 'CallExpression') {
                if (
                  currentNode.callee.type === 'MemberExpression' &&
                  currentNode.callee.property.type === 'Identifier'
                ) {
                  const methodName = currentNode.callee.property.name;

                  // 检查是否找到了链式调用的起点（with方法）
                  if (
                    methodName === 'with' &&
                    currentNode.callee.object.type === 'Identifier' &&
                    currentNode.callee.object.name === 'PlatformChain'
                  ) {
                    chainStart = currentNode;
                    if (currentNode.arguments && currentNode.arguments.length > 0) {
                      defaultCallback = currentNode.arguments[0];
                    }
                    break;
                  }

                  // 收集平台方法，使用CodePlatformMap映射到正确的平台标识符
                  if (Object.keys(CodePlatformMethodMap).includes(methodName)) {
                    if (currentNode.arguments && currentNode.arguments.length > 0) {
                      const platformKey = CodePlatformMethodMap[methodName as CodePlatformHumpMethod];
                      platformCallbacks.set(platformKey, currentNode.arguments[0]);
                    }
                  } else if (methodName === 'case' && currentNode.arguments && currentNode.arguments.length > 1) {
                    // 支持case方法，第一个参数是平台数组，第二个参数是回调函数
                    if (currentNode.arguments[0].type === 'ArrayExpression' && currentNode.arguments[0].elements) {
                      const platforms = currentNode.arguments[0].elements
                        .filter((element: any) => element.type === 'StringLiteral')
                        .map((element: any) => element.value);

                      const callback = currentNode.arguments[1];
                      platforms.forEach((platform: string) => {
                        if (Object.keys(CodePlatformMethodMap).includes(platform)) {
                          const platformKey = CodePlatformMethodMap[platform as CodePlatformHumpMethod];
                          platformCallbacks.set(platformKey, callback);
                        }
                      });
                    }
                  }
                }

                // 继续向上查找链式调用
                if (currentNode.callee && currentNode.callee.object) {
                  currentNode = currentNode.callee.object;
                } else {
                  break;
                }
              }

              // 如果找到了链式调用的起点
              if (chainStart) {
                // 确定哪个回调应该被保留，按照与PlatformChain.ts中execute()方法相同的优先级
                let resultCallback = null;

                // 优先检查当前平台
                if (platformCallbacks.has(currentPlatform)) {
                  resultCallback = platformCallbacks.get(currentPlatform);
                }
                // 检查通用小程序
                else if (isMp && platformCallbacks.has('MP')) {
                  resultCallback = platformCallbacks.get('MP');
                }
                // 使用默认回调
                else if (defaultCallback) {
                  resultCallback = defaultCallback;
                }

                // 如果找到了匹配的回调，替换整个PlatformChain调用
                if (resultCallback) {
                  // 创建一个函数调用，将exe()的参数传递给选中的回调函数
                  const replacement = babel.types.callExpression(resultCallback, exeArguments);
                  path.replaceWith(replacement);
                }
              }
            }
          }
        }
      };
    };
  }

  /**
   * 创建webpack加载器，用于在构建时应用代码转换
   * @param platform 平台
   * @returns webpack加载器配置
   */
  public static createWebpackLoader(platform: CodePlatformMethod) {
    return {
      loader: 'babel-loader',
      options: {
        plugins: [PlatformTransformer.createBabelPlugin(platform)]
      }
    };
  }

  /**
   * 提取Vue/Nvue/Uvue文件中的脚本内容
   * @param code 文件内容
   * @returns 提取的脚本内容和脚本标签索引位置
   */
  private static extractScriptContent(code: string): {
    scriptContent: string,
    startIdx: number,
    endIdx: number
  } | null {
    // 匹配<script>标签内容，包括带有lang="ts"等属性的情况
    const scriptMatch = code.match(/<script(\s+[^>]*)?>([\s\S]*?)<\/script>/i);

    if (scriptMatch) {
      const fullMatch = scriptMatch[0];
      const scriptContent = scriptMatch[2];
      const startIdx = code.indexOf(fullMatch);
      const endIdx = startIdx + fullMatch.length;

      return {
        scriptContent,
        startIdx,
        endIdx
      };
    }

    return null;
  }

  /**
   * 创建Vite插件，用于在构建时应用代码转换
   * @param platform 平台
   * @returns Vite插件配置
   */
  public static createVitePlugin(platform: CodePlatformMethod) {
    const babelPlugin = PlatformTransformer.createBabelPlugin(platform);

    return {
      name: 'vite-plugin-platform-transform',
      transform(code: string, id: string) {
        // 处理JavaScript和TypeScript文件
        if (/\.(ts|js|tsx|jsx)$/.test(id)) {
          // 使用babel转换代码
          const result = babel.transform(code, {
            filename: id,
            plugins: [babelPlugin]
          });

          return result ? {
            code: result.code || '',
            map: result.map
          } : null;
        }

        // 处理Vue、Nvue
        if (/\.(vue|nvue)$/.test(id)) {
          // 提取<script>标签内容
          const scriptInfo = PlatformTransformer.extractScriptContent(code);

          if (scriptInfo) {
            const {scriptContent, startIdx, endIdx} = scriptInfo;

            // 使用babel转换脚本内容
            const result = babel.transform(scriptContent, {
              filename: id + '.ts', // 添加.ts后缀以支持TypeScript语法
              plugins: [babelPlugin]
            });

            if (result && result.code) {
              // 替换原始脚本内容
              const scriptTagStart = code.substring(startIdx, startIdx + code.substring(startIdx).indexOf('>') + 1);
              const transformedCode =
                code.substring(0, startIdx) +
                scriptTagStart +
                result.code +
                '</script>' +
                code.substring(endIdx);

              return {
                code: transformedCode,
                map: result.map
              };
            }
          }
        }

        return null;
      }
    };
  }

  /**
   * 创建HBuilderX插件所需的处理函数
   * @param platform 平台
   * @returns 处理函数
   */
  public static createHBuilderXPlugin(platform: CodePlatformMethod) {
    const babelPlugin = PlatformTransformer.createBabelPlugin(platform);

    /**
     * 处理单个文件
     * @param filePath 文件路径
     * @param content 文件内容
     * @returns 处理后的内容
     */
    const processFile = (filePath: string, content: string): string | null => {
      // 处理JavaScript和TypeScript文件
      if (/\.(ts|js|tsx|jsx)$/.test(filePath)) {
        // 使用babel转换代码
        const result = babel.transform(content, {
          filename: filePath,
          plugins: [babelPlugin]
        });

        return result ? result.code || null : null;
      }

      // 处理Vue、Nvue和Uvue文件
      if (/\.(vue|nvue|uvue)$/.test(filePath)) {
        // 提取<script>标签内容
        const scriptInfo = PlatformTransformer.extractScriptContent(content);

        if (scriptInfo) {
          const {scriptContent, startIdx, endIdx} = scriptInfo;

          // 使用babel转换脚本内容
          const result = babel.transform(scriptContent, {
            filename: filePath + '.ts', // 添加.ts后缀以支持TypeScript语法
            plugins: [babelPlugin]
          });

          if (result && result.code) {
            // 替换原始脚本内容
            const scriptTagStart = content.substring(startIdx, startIdx + content.substring(startIdx).indexOf('>') + 1);
            return content.substring(0, startIdx) +
              scriptTagStart +
              result.code +
              '</script>' +
              content.substring(endIdx);
          }
        }
      }

      return null;
    };

    return {
      processFile,
      platform
    };
  }
}
